//
// Created by 11067 on 2023/3/6.
//

#include <cstring>
#include <iostream>
#include "OptionParser.h"
#include "exceptions/WordListException.h"

/**
 * 验证扩展名
 * @return 是否合法
 */
static bool checkExt(char *filename) {
    //std::cout << filename << std::endl;
    //小心.表示当前文件夹
    char *pos = strrchr(filename, '.');
    if (pos == nullptr) {
        return false;
    }
    char *ext = pos + 1;
    return strcmp(ext, "txt") == 0;
}

static char checkAlphabet(char c) {
    if ('A' <= c && c <= 'Z') {
        return c + ('a' - 'A');
    } else if ('a' <= c && c <= 'z') {
        return c;
    } else {
        throw CLIOptionException("only [a-zA-Z] is allowed for option.");
    }
}

int OptionParser::parse(int argc, char *argv[], Options &options) {
    bool basicOptionSpecified = false; // 是否已经指定了基础选项
    bool headSpecified = false;
    bool tailSpecified = false;
    bool headNotAllowedSpecified = false;
    bool cycleSpecified = false;
    if (argc < 3) { // 至少有基础选项
        throw CLIOptionException("too few options");
    }

    for (int i = 1; i < argc;) {
        char *func = argv[i];
        size_t len = std::strlen(func);
        if (len != 2 || func[0] != '-') {
            throw CLIOptionException("invalid option");
        }
        char specifier = func[1];
        i = i + 1;
        switch (specifier) {
            case 'n': {
                if (basicOptionSpecified) {
                    throw CLIOptionException("file name has been specified more than once");
                }
                char *filename = argv[i];
                if (!checkExt(filename)) {
                    throw CLIOptionException("file name invalid, should be txt.");
                }

                options.filename = filename;
                options.countType = CountType::WordChainCount;
                basicOptionSpecified = true;
                break;
            }
            case 'w': {
                if (basicOptionSpecified) {
                    throw CLIOptionException("file name has been specified more than once");
                }
                char *filename = argv[i];
                if (!checkExt(filename)) {
                    throw CLIOptionException("file name invalid, should be txt.");
                }

                options.filename = filename;
                options.countType = CountType::MaxWordLengthWordChain;
                basicOptionSpecified = true;
                break;
            }
            case 'c': {
                if (basicOptionSpecified) {
                    throw CLIOptionException("file name has been specified more than once");
                }
                char *filename = argv[i];
                if (!checkExt(filename)) {
                    throw CLIOptionException("file name invalid, should be txt.");
                }
                options.filename = filename;
                options.countType = CountType::MaxAlphabetLengthWordChain;
                basicOptionSpecified = true;
                break;
            }
            case 'h': {
                if (headSpecified) {
                    throw CLIOptionException("-h option has been specified more than once.");
                }
                char *cc = argv[i];
                if (std::strlen(cc) != 1) {
                    throw CLIOptionException("should specify only ONE character for -h.");
                }
                options.beginWith = checkAlphabet(cc[0]);
                headSpecified = true;
                break;
            }
            case 't': {
                if (tailSpecified) {
                    throw CLIOptionException("-t option has been specified more than once.");
                }
                char *cc = argv[i];
                if (std::strlen(cc) != 1) {
                    throw CLIOptionException("should specify only ONE character for -t.");
                }
                options.endWith = checkAlphabet(cc[0]);
                tailSpecified = true;
                break;
            }
            case 'j': {
                if (headNotAllowedSpecified) {
                    throw CLIOptionException("-j option has been specified more than once.");
                }
                char *cc = argv[i];
                if (std::strlen(cc) != 1) {
                    throw CLIOptionException("should specify only ONE character for -j.");
                }
                options.notBeginWith = checkAlphabet(cc[0]);
                headNotAllowedSpecified = true;
                break;
            }
            case 'r': {
                if (cycleSpecified) {
                    throw CLIOptionException("-r option has been specified more than once.");
                }
                i = i - 1; // -r has no arg
                options.isCycleAllowed = true;
                cycleSpecified = true;
                break;
            }
            default: {
                throw CLIOptionException("option not recognized.");

            }
        }
        i = i + 1;
    }
    if (!basicOptionSpecified) {
        throw CLIOptionException("no file specified.");

    }
    if ((options.countType == CountType::WordChainCount) &
        ((options.notBeginWith != '\0') |
         (options.beginWith != '\0') |
         (options.endWith != '\0') |
         options.isCycleAllowed)) {
        // https://github.com/linux-test-project/lcov/pull/80
        throw CLIOptionException("-n should not be used with other options.");
    }
    return 0;
}
